package org.scheduler4j;

import java.util.Calendar;
import java.util.Date;

import org.scheduler4j.util.DateUtil;

public class Week extends DayDuration {
	private int startDay;

	/**
	 * constructor with start day and the start date of the week.
	 * 
	 * @param day
	 * @param weekStartDay
	 */
	public Week(Day day, int weekStartDay) {
		setStartDate(getNearestDayBefore(day, weekStartDay));
		setEndDate(new Day(DateUtil.afterDays(getStartDate().toDate(), 6)));
		this.startDay = weekStartDay;
	}

	/**
	 * constructor with start day, the start date of the week should be Sunday.
	 * 
	 * @param date
	 */
	public Week(Date date) {
		this(date, Constants.WEEK_DEFAULT_START_DAY);
	}

	/**
	 * constructor with start Date and start week date.
	 * 
	 * @param date
	 * @param weekStartDay
	 */
	public Week(Date date, int weekStartDay) {
		this(new Day(date), weekStartDay);
	}

	/**
	 * constructor with year, week number and start week date.
	 * 
	 * @param year
	 * @param weekNumber
	 * @param weekStartDay
	 */
	public Week(int year, int weekNumber, int weekStartDay) {
		Day day = new Day(DateUtil.firstDayOfYear(year));
		for (int i = 0; i < 7; i++) {
			if (DateUtil.getDayOfWeek(day.toDate()) == weekStartDay) {
				break;
			}
			day = day.tomorrow();
		}
		day = day.after((weekNumber - 1) * 7);
		if (day.getYear() != year) {
			throw new InvalidWeekException();
		}
		setStartDate(day);
		this.startDay = weekStartDay;
	}

	/**
	 * constructor with start date.
	 * 
	 * @param day
	 */
	public Week(Day day) {
		this(day, Constants.WEEK_DEFAULT_START_DAY);
	}

	public int getWeekNo() {
		return DateUtil.getWeekNo(getStartDate().toDate());
	}

	private Day getNearestDayBefore(Day day, int weekStartDay) {
		Day result = new Day();
		switch (weekStartDay) {
		case Calendar.SUNDAY:
			result = new Day(DateUtil.getNearestSundayBefore(day.toDate()));
			break;
		case Calendar.MONDAY:
			result = new Day(DateUtil.getNearestMondayBefore(day.toDate()));
			break;
		case Calendar.TUESDAY:
			result = new Day(DateUtil.getNearestTuesdayBefore(day.toDate()));
			break;
		case Calendar.WEDNESDAY:
			result = new Day(DateUtil.getNearestWednesdayBefore(day.toDate()));
			break;
		case Calendar.THURSDAY:
			result = new Day(DateUtil.getNearestThursdayBefore(day.toDate()));
			break;
		case Calendar.FRIDAY:
			result = new Day(DateUtil.getNearestFridayBefore(day.toDate()));
			break;
		case Calendar.SATURDAY:
			result = new Day(DateUtil.getNearestSaturdayBefore(day.toDate()));
			break;
		default:
			throw new InvalidWeekException("invalid week date " + weekStartDay);
		}
		return result;
	}

	public int getStartDay() {
		return startDay;
	}

	public void setStartDay(int startDay) {
		this.startDay = startDay;
	}

	@Override
	public int hashCode() {
		return getStartDate().hashCode() + getEndDate().hashCode();
	}

	@Override
	public String toString() {
		return "Week[" + DateUtil.getYear(getStartDate().toDate()) + "-"
				+ getWeekNo() + "]";
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null || !(obj instanceof Week)) {
			return false;
		}
		Week tmp = (Week) obj;
		return tmp.getStartDate().isSameDay(getStartDate());
	}
}
